#pragma once
#include <string>
#include <iostream>
#include "Mutex.hpp"
#include <filesystem>
#include <fstream>
#include <memory>
#include<ctime>
#include<sstream>
#include<unistd.h>

namespace kzz
{
    const std::string gsep = "\r\n";

    // 多态，重写刷新策略
    class LogStrategy
    {
    public:
        ~LogStrategy() = default;
        virtual void SyncLog(const std::string &massage) = 0;
    };
    // 显示器刷新
    class ConsoleLogStrategy : public LogStrategy
    {
    public:
        void SyncLog(const std::string &massage) override
        {
            kzz::LockGuard lock(_mutex);
            std::cout << massage<<std::endl;
        }

    private:
        kzz::Mutex _mutex;
    };
    const std::string defaultpath = "./LLL";
    const std::string defaultfile = "log.log";

    // 指定文件刷新
    class FileLogStrategy : public LogStrategy
    {
    public:
     

        FileLogStrategy(const std::string path = defaultpath, const std::string file = defaultfile)
        :_path(path)
        ,_file(file)
        {
            kzz::LockGuard lock(_mutex);
            // std::filesystem::path _path = "test.txt";
            // 如果目录路径存在就直接返回，没有就创建目录
            if (std::filesystem::exists(_path))
            {
                return;
            }
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                
                std::cerr << e.what() << '\n';
            }
        }
        void SyncLog(const std::string &massage) override
        {
            // 加锁，防止其他进程创建文件，文件也是临界i资源
            kzz::LockGuard lock(_mutex);
            std::string filename = _path + ((_path.back() == '/') ? "" : "/") + _file;
            std::ofstream outfile(filename.c_str(), std::ios::app);
            // 打不开就走
            if (!outfile.is_open())
                return;

            outfile << massage << gsep;
            // 写完要关闭文件
            outfile.close();
        }

    private:
        kzz::Mutex _mutex;
        std::string _path;
        std::string _file;
    };

    // 定义日志等级
    enum class LogLevel
    {
        DBUGER,
        ERROR,
        FATAIL,
        WORING
    };
    std::string GetCurTime()
    {
        std::time_t cur;
        std::time(&cur);
        std::tm *_local_time=std::localtime(&cur);
        
        char ret[128];
        snprintf(   ret,sizeof(ret),"%4d/%02d/%02d/%02d-%02d-%02d",
        _local_time->tm_year+1900,
        _local_time->tm_mon+1,
        _local_time->tm_mday,
        _local_time->tm_hour,
        _local_time->tm_min,
        _local_time->tm_sec   );
        return ret;

    } 
     std::string LevelToStr(const LogLevel& level)
    {
        switch(level)
        {
            case  LogLevel::DBUGER:
            return "DBUGER";
            case  LogLevel::ERROR:
            return "ERROR";
            case  LogLevel::FATAIL:
            return "FATAIL";
            case  LogLevel::WORING:
            return "WORING";
            default:
            return "NONE";
        }
    }
    class Logger
    {
    public:
        Logger()
        {
            //默认项显示器刷新
            EnableConsoleLogStrategy();
        }
        void EnableConsoleLogStrategy()
        {
            ///错误，不能类型赋给类型
            // _fflush_Strategy=std::make_unique<ConsoleLogStrategy>；
            ////////////
            //子类对象可以赋值给父类对象
            _fflush_Strategy=std::make_unique<ConsoleLogStrategy>();
        }
        void EnableFileLogStrategy()
        {
            _fflush_Strategy=std::make_unique<FileLogStrategy>();
        }



        //一个LogMassage对象就是一条消息
        //在类内定义是为了传this指针
        class LogMassage
        {
            public:
            LogMassage(const LogLevel &level,std::string name,int line,kzz::Logger *logger)
            :_cur_time(GetCurTime())
            ,_level(level)
            ,_pid(getpid())
            ,_src_name(name)
            ,_line_number(line)
            ,_logger(logger)
            {
                //日志的左边
                std::stringstream ss;
                ss<<'['<<_cur_time<<']'
                <<'['<<LevelToStr(level)<<']'
                <<'['<<_pid<<']'
                <<'['<<_src_name<<']'
                <<'['<<_line_number<<']';
                //类声明周期结束要输出_loginfo
                //这句必须要写，如果没有<<操作，当前句结束后也会输出日志的左边
                _loginfo=ss.str();
            }
            //返回值是自己方便多次<<
            template <class T>
            LogMassage& operator<<(const T& massage)
            {
                //不知道massage是什么类型，只能先往流里面写
                std::stringstream ss;
                ss<<massage;
                

                _loginfo+=ss.str();
                return *this;
            }
            ~LogMassage()
            {
                //
                //_logger->_fflush_Strategy(_loginfo);
                _logger->_fflush_Strategy->SyncLog(_loginfo);
            }
            private:
            std::string _cur_time;
            const LogLevel _level;
            pid_t  _pid;
            std::string _src_name;
            int _line_number;
            std::string _loginfo;
            kzz::Logger *_logger;

        };
        //用()操作构造LogMassage对象
        //返回临时对象出了当前行就销毁
        LogMassage operator()(const LogLevel &level,std::string name,int line)
        {
            // LogMassage(const LogLevel &level,std::string name,int line,kzz::Logger *logger)
            return  LogMassage(level,name,line,this);
        }
         ~Logger()
        {
        }
    private:
       
        std::unique_ptr<LogStrategy> _fflush_Strategy;
    };



    //全局对象
    Logger log;
    //EnableConsoleLogStrategy();
    //void EnableFileLogStrategy()
    //简化用户操作，只有一个Logger对象，用()操作生产massage对象，massage对象生命周期只在当前行
    #define LOG(level)        log.operator()(level,__FILE__,__LINE__)
    #define ENABLE_CONSOLE_STRATEGY()   log.EnableConsoleLogStrategy()
    #define ENABLE_FILE_STRATEGY()      log.EnableFileLogStrategy()


}
